﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tao.OpenGl;
using AvantGameMachine.Graphics.Animators;

namespace AvantGameMachine.Graphics
{
    public class IRenderizableObject : Transformable
    {
        protected string name = string.Empty;
        internal RenderList children = new RenderList();
        private IRenderizableObject parent = null;

        private float rOrder = 0.0f;
        private bool needsBlend = false;
        private Camera parentCamera = null;
        private bool is2D;
        private bool inBlendMode = false;

        private List<Animator> animatorsList = new List<Animator>();

        public IRenderizableObject()
        {
            pChanged = this.PositionChanged;
        }

        public IRenderizableObject Parent
        {
            get { return parent; }
        }

        public bool ChildrenTransformed
        {
            get
            {
                if (TransformationChanged)
                {
                    return true;
                }

                return children.AreChildrenTransformed();
            }
        }

        public void SetProperty(int _prop, object _propValue)
        {
            if (_prop > Transformable.TRANS_LAST)
            {
            }
            else
            {
                SetTransformableProperty(_prop, (PosVector)_propValue);
            }
        }

        private void PositionChanged()
        {
            rOrder = position.Z;
            if (parent != null)
                parent.children.Reorder(this);
        }

        public Camera ParentCamera
        {
            get { return parentCamera; }
        }

        public bool NeedsBlend
        {
            get { return needsBlend;  }
            set { needsBlend = value; }
        }

        public float ROrder
        {
            get { return rOrder; }
            set { rOrder = value; }
        }

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public virtual PosVector Max
        {
            get { return new PosVector(0.0f, 0.0f, 0.0f); }
        }

        public virtual PosVector Min
        {
            get { return new PosVector(0.0f, 0.0f, 0.0f); }
        }

        public virtual PosVector Length
        {
            get
            {
                PosVector min = Min;
                PosVector max = Max;

                return max - min;
            }
        }

        public void SetParentCamera(Camera _newParentCamera,bool _is2D)
        {
            parentCamera = _newParentCamera;
            is2D = _is2D;
        }

        public void AddAndStartAnimator(Animator _anim)
        {
            AddAnimator(_anim);
            _anim.Start();
        }

        public void AddAnimator(Animator _anim)
        {
            _anim.AnimObject = this;
            animatorsList.Add(_anim);
        }

        public T GetChildByIndex<T>(int _index) where T : IRenderizableObject
        {
            return (T)children.GetByIndex(_index);
        }

        public override void PreRender()
        {
            base.PreRender();
            AGMPlatform.TopScene.renderLevel++;

            if (needsBlend)
            {
                inBlendMode = true;
                Gl.glPushAttrib(Gl.GL_ALL_ATTRIB_BITS);
                Gl.glEnable(Gl.GL_BLEND);
				Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);
            }
        }

        public override void PostRender()
        {
            AGMPlatform.TopScene.renderLevel--;


            if (inBlendMode)
            {
                Gl.glPopAttrib();
            }

            base.PostRender();
        }

        protected virtual void PrivateUpdate()
        {
        }

        private void UpdateAnimators()
        {
            if (animatorsList.Count > 0)
            {
                foreach (Animator anim in animatorsList)
                {
                    anim.OnAnimate();
                }
            }
        }

        public virtual void Render()
        {
            AGMPlatform.TopScene.renderizedObjects++;
            AGMPlatform.TopScene.AddObjectToDebugRender(this);
            UpdateAnimators();
            PrivateUpdate();
        }

        public void InternalRender()
        {
            PreRender();
            Render();
            RenderChildren();
            PostRender();
        }

        public int NumChildren
        {
            get { return children.Count; }
        }

        public virtual void RenderChildren()
        {
            children.InternalRender();
        }

        public T CreateNew<T>(string _name) where T : IRenderizableObject, new()
        {
            T obj = new T();
            obj.name = _name;
            obj.SetParent(this);
            obj.parentCamera = parentCamera;
            return obj;
        }

        private void RemoveChild(IRenderizableObject _obj)
        {
            children.Remove(_obj);
        }

        private void AddChild(IRenderizableObject _obj)
        {
            children.Add(_obj);
            _obj.parent = this;
        }

        internal void DirectSetParent(IRenderizableObject _newParent)
        {
            parent = _newParent;
        }

        public void SetParent(IRenderizableObject _newParent)
        {
            if (parent != null)
            {
                parent.RemoveChild(this);
                parent = null;
            }

            if (_newParent != null)
            {
                _newParent.AddChild(this);
            }
        }

        public virtual BoundingCube BoundCube
        {
            get
            {
                return new BoundingCube(this);
            }
        }

        public PosVector ConvertPointsToProjection(PosVector _pos)
        {
            return new PosVector(ConvertPointsToProjectionX(_pos.X), ConvertPointsToProjectionY(_pos.Y), ConvertPointsToProjectionX(_pos.Z));
        }

        internal float ConvertPointsToProjectionX(float _x)
        {
            return parentCamera.ConvertPointsToProjectionX(_x, is2D);
        }

        internal float ConvertPointsToProjectionY(float _y)
        {
            return parentCamera.ConvertPointsToProjectionY(_y, is2D);
        }

        internal float ConvertPointsToProjectionZ(float _z)
        {
            return parentCamera.ConvertPointsToProjectionZ(_z, is2D);
        }

        protected virtual void DisposeObject()
        {
        }

        protected virtual void Dispose(bool _disposing)
        {
            if (_disposing)
            {
//                AGMPlatform.log.Debug("Disposing object <" + this.name + "> explicitly");
            }
            else
            {
//                AGMPlatform.log.Debug("Disposing object <" + this.name + "> implicitly");
            }

            DisposeObject();

            if (_disposing)
            {
                if (this.parent != null)
                {
                    this.SetParent(null);
                }
                IRenderizableObject[] obja = children.ToArray();
                for (int i = 0; i < obja.Length; i++)
                {
                    obja[i].Dispose();
                }
            }
            children.Clear();
        }

        public void RemoveChildrenFromRender()
        {
            IRenderizableObject[] arr = children.ToArray();
            foreach (IRenderizableObject obj in arr)
            {
                obj.SetParent(null);
            }
        }

        public bool DisposeChild(IRenderizableObject _obj)
        {
            _obj.SetParent(null);
            _obj.Dispose(true);
            return true;
        }

        public void DisposeChildren()
        {
            IRenderizableObject[] arr = children.ToArray();
            foreach (IRenderizableObject obj in arr)
            {
                DisposeChild(obj);
            }
        }


        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~IRenderizableObject()
        {
            Dispose(false);
        }
    }
}
